1. Introducción

En Colombia, el Registro Único Nacional de Tránsito (RUNT), funciona como una gran base de datos centralizada en la que se almacena información sobre todos los vehículos del país, los conductores, los seguros de estos, las infracciones entre muchos más. El presente reporte asume el ejercicio de crear un modelo estadístico para predecir el número de vehículos registrados diariamente en el RUNT durante el año 2018.

2. Datos

La base de datos inicial, registro_autos_entrenamiento, en formato .XLSX, contiene los registros diarios de autos en el RUNT, desde las fechas 1/01/2012 hasta 31/12/2017. Se usaron los software Excel y R para el procesamiento de la base de datos. Inicialmente, la base de datos contiene 2192 observaciones y 2 variables. Las variables Fecha y Unidades. Una vista previa de las primeras observaciones de la base de datos es la siguiente:
datos <- read_excel("registros_autos_entrenamiento.xlsx") #cargar datos

head(datos, n=5)

Con el objetivo de plantear un modelo más interesante, se propone la creación de cinco nuevas variables que permitirán desarrollar un modelo predictivo que arroje más información en sus resultados teniendo en cuenta el año, mes, días del mes, días de la semana y si el día tuvo o no alguna festividad. Las variables creadas son:

YEAR: año del registro. DIA: día del mes del registro.
MES: mes del registro. DIA_SEMANA: día del registro (lunes a domingo) FESTIVIDAD: indica si el registro se realizó en alguna de las siguientes fechas especiales: a_nuevo: 1 de enero de cada año. día_habil: día en donde no es festivo no hay festividades. festivo: día festivo. sem_santa: día de semana santa, de sábado a domingo. madres: día de las madres. brujas: día de las brujas, 31 de octubre. navidad: 25 de diciembre. a_viejo: 31 de diciembre.

Todas las nuevas variables exceptuando FESTIVIDAD se obtienen de la variable Fecha. En el caso de la variable FESTIVIDAD, se obtiene a través una base de datos externa que se adiciona a la base de análisis original y abarca los días feriados en Colombia desde 2012 hasta 2018.

Para efectos practicos en la sección de modelo predictivo, se exportan dos bases de datos:

La primera, con 7 variables y 2192 observaciones que van desde el año 2012 hasta el año 2017.

La segunda, con 6 variables y 2575 observaciones que van desde el año 2012 hasta el año 2018. Esta base de datos no contiene la variable UNIDADES debido a que se borró intencionalmente para luego rellenarla con la predicción del modelo.

3. Análisis descriptivo

plot_ly(data = descriptivo, x = ~FECHA, y = ~UNIDADES,
        type = "scatter", mode = "lines", split = ~YEAR,
        line = list(width = 1)) %>%
  layout(title = 'Registros de vehiculos diarios en el RUNT de 2012-2017',
        xaxis = list(title = "Año"),
        yaxis = list(title = "Número de vehiculos"))

Aprecia que gradualmente van disminuyendo los registros diarios, se registran cada vez más vehículos los últimos días de cada mes durante todo el año, el promedio diario de vehículos registrados parece mantenerse ya que el último día del mes compensa la falta de registros del resto de días pero en realidad el registro de vehículos si disminuye año por año ya que esta compensación no es suficiente Además, en esta grafica se puede apreciar una gran cantidad de días en los cuales no se registran vehículos, en los cuales resaltar cada 1 de enero y 25 de diciembre.

descriptivo$DIA_SEMANA <- ordered(descriptivo$DIA_SEMANA, levels = c("lunes","martes","miercoles","jueves","viernes","sabado","domingo"))


totals5 <- descriptivo %>%
  group_by(DIA_SEMANA) %>%
  mutate(total=sum(UNIDADES))
totals5
ggplot(descriptivo, aes(fill = DIA_SEMANA, x =  DIA_SEMANA, y = UNIDADES)) +
  geom_bar(stat = "identity") +
  geom_text(data = totals5, aes( y = total, label = total), vjust = -0.5) +
  scale_y_continuous(breaks = seq( 0,450000,50000)) +
  xlab("Días") + 
  ylab("Numero de vehiculos") + 
  ggtitle("Registro de vehiculos por día de la semana") +
  theme(legend.position = "none")

Podemos apreciar que de miércoles a viernes se hace la mayoría de los registros, con el viernes como el día de la semana con mayor cantidad de vehículos registrados, se puede observar como hay menos registros en los fines de semana en comparación a los días de la semana, ademas de que el domingo es el día con menor cantidad de registros de vehículos.

totals4 <- descriptivo %>%
  group_by(MES) %>%
  mutate(total=sum(UNIDADES))


ggplot(descriptivo, aes(fill = MES, x = as.factor(MES), y = UNIDADES)) + 
  geom_bar(stat = "identity") +
  geom_text(data = totals4, aes( y = total, label = total), vjust = -0.5) +
  scale_y_continuous(breaks = seq( 0,200000,25000)) +
  scale_fill_viridis_d( option = "G") +
  xlab("Mes") +
  ylab("Numero de vehiculos") +
  ggtitle("Registro de vehiculos por mes")+
  theme(legend.position = "none")

Diciembre es el mes con mayor cantidad de registros de vehículos y enero el mes con menor cantidad de registros, ignorando estos dos datos y solo tomando del mes febrero al mes de noviembre los registros de vehículos en el RUNT están entre 130000 y 150000, variando por unos cuantos miles de vehículos

totals3 <- descriptivo %>%
  group_by(YEAR) %>%
  mutate(total=sum(UNIDADES))


ggplot(descriptivo, aes(fill = YEAR, x = as.factor(YEAR), y = UNIDADES)) + 
  geom_bar(stat = "identity") +
  geom_text(data = totals3, aes( y = total, label = total), vjust = -0.5) +
  scale_y_continuous(breaks = seq( 0,400000,25000)) +
  scale_fill_viridis_d( option = "E") +
  xlab("Año") +
  ylab("Numero de vehiculos") +
  ggtitle("Registro de vehiculos  por año")+
  theme(legend.position = "none")

El año 2014 es el año con más vehículos registrados, ignorando este dato podría decirse que cada año se registran menos vehículos.

totals2 <- descriptivo %>%
  group_by(dia_habil) %>%
  mutate(total=sum(UNIDADES))

gfes1 <- ggplot(descriptivo, aes(fill = dia_habil, x = dia_habil, y = UNIDADES)) +
  geom_bar(stat = "identity") +
  geom_text(data = totals2, aes(x = dia_habil, y = total, label = total), vjust = -0.5) +
  scale_y_continuous(breaks = seq( 0,2250000,250000)) +
  xlab("dia habil") +
  ylab("Numero de vehiculos") +
  ggtitle("registros de dias habiles y no habiles")+
  theme(legend.position = "none")

totals <- solo_tipo_festividad %>%
  group_by(FESTIVIDAD) %>%
  mutate(total=sum(UNIDADES))


gfes2 <- ggplot(solo_tipo_festividad, aes(fill = FESTIVIDAD, x = reorder(FESTIVIDAD,+UNIDADES), y = UNIDADES)) +
  geom_bar(stat = "identity") +
  scale_fill_viridis_d( option = "D") +
  geom_text(data = totals, aes(x = FESTIVIDAD, y = total, label = total), vjust = -0.5) +
  xlab("festividad") +
  ylab("Numero de vehiculos") +
  scale_y_continuous(breaks = seq( 0,30000,2500)) +
  ggtitle("Registros en las festividades") +
  theme(axis.text.x = element_text(angle = 90, hjust = 0), legend.position = "none")


ggarrange(gfes1,gfes2)

A comparación de los días hábiles, la suma de vehículos registrado en los días festivos y de festividades es muy poca. Los dos unicos días que no presentan ningún registro de vehículos en el RUNT de 2012 a 2017 son año nuevo(primero de enero) y navidad(25 de diciembre).

Siendo muchos menos los días festivos comunes que los días celebración de la semana santa se registran muchos más vehículos en semana santa, puede hacerse una comparación parecida con el día de madres y haloween, siendo solo un día hay una enorme diferencia en los registros siendo el día de madres 2 registros al ser día domingo y en haloween más de 11mil al ser casi siempre un día normal en semana.

4. Modelo Predictivo

Se lleva a cabo un Modelo lineal generalizado con la familia de distribución Poisson, donde la variable respuesta (variable dependiente) sería “UNIDADES”, y las variables explicativas (variables independientes) son: FECHA, YEAR, DIA, MES, DIA_SEMANA, y FESTIVIDAD.

Donde se utilizarán los años desde 2012 hasta 2016 para la creación del modelo.

Se procede a hacer lectura de la base de datos en donde se almacenarán todas las predicciones, según el modelo anterior.

Se muestran las primeras 10 observaciones de las predicciones dadas. Almacenadas en la variable NRO_TOTAL_VEHICULOS, que representa el número de vehículos registrados.

prediccion <- predict(object = lm2, newdata = Base_prediccion,
                          type = "response")

prediccion_diaria<- Base_prediccion %>% 
  mutate(Nro_Vehiculos = round(prediccion,0))

diario_20_02 <- prediccion_diaria %>%
  group_by(FECHA, YEAR, DIA, MES, DIA_SEMANA, FESTIVIDAD) %>%
  mutate(NRO_TOTAL_VEHICULOS=Nro_Vehiculos)

diario_20_02$NRO_TOTAL_VEHICULOS <- as.integer(diario_20_02$NRO_TOTAL_VEHICULOS)

head(diario_20_02, 10)

Luego, se observa el MSE, MAE y \(R^2\) para los datos de entrenamiento (que posee los días comprendidos entre el 01/01/2012 y el 31/12/2016).

#Entrenamos con 2012-2016

datos_02 <- subset(los_datos, (YEAR != '2017'))

y_train <- round(predict(lm2, newdata= datos_02, type="response"))
y_actual <- datos_02$UNIDADES
lm7_tmse <- MSE(y_train, y_actual)
lm7_tmae <-  MAE(y_train, y_actual)
lm7_r2 <- R2_Score(y_train, y_actual)
sprintf("MSE: %f, MAE: %f, R2 Score: %f", 
        lm7_tmse, lm7_tmae, lm7_r2)
## [1] "MSE: 55163.222770, MAE: 150.340449, R2 Score: 0.818973"

Por consiguiente, se observa el MSE, MAE y \(R^2\) para los datos de validación (que posee los días comprendidos entre el 01/01/2017 y el 31/12/2017).

#Validación con 2017

datos_vl <- subset(los_datos, (YEAR == '2017'))

y_train <- round(predict(lm2, newdata= datos_vl, type="response"))
y_actual <- datos_vl$UNIDADES
lm7_tmse <- MSE(y_train, y_actual)
lm7_tmae <-  MAE(y_train, y_actual)
lm7_r2 <- R2_Score(y_train, y_actual)
sprintf("MSE: %f, MAE: %f, R2 Score: %f", lm7_tmse, lm7_tmae, lm7_r2)
## [1] "MSE: 82417.772603, MAE: 166.572603, R2 Score: 0.704797"

También se calcula el MSE, MAE y \(R^2\) para el primer semestre de 2017.

# Primer semestre del 2017

datos_vl <- subset(los_datos, (YEAR == '2017'))
sem2017<- datos_vl[c(1:181),]

y_train <- round(predict(lm2, newdata= sem2017, type="response"))
y_actual <- sem2017$UNIDADES
lm7_tmse <- MSE(y_train, y_actual)
lm7_tmae <-  MAE(y_train, y_actual)
lm7_r2 <- R2_Score(y_train, y_actual)
sprintf("MSE: %f, MAE: %f, R2 Score: %f", lm7_tmse, lm7_tmae, lm7_r2)
## [1] "MSE: 68703.745856, MAE: 145.447514, R2 Score: 0.705406"

Luego, el \(R^2\) del conjunto de validación que comprende el primer semestre del 2018 (01/01/2018 y el 30/06/2018), no se calcula ya que para el 2018 no se obtienen observaciones reales para dicho año.

A continuación, se aprecian las primeras 10 y las últimas 10 observaciones del primer archivo plano (01/01/2012 y el 31/12/2017), que contiene 1827 observaciones con dos variables.

datos_16 <- subset(diario_20_02, (YEAR != '2018'))
dat <- datos_16[,-2]
dat01 <- dat[,-2]
dat02 <- dat01[,-2]
dat03 <- dat02[,-2]
datos_primer_archivo_plano <- dat03[,-2]
head(datos_primer_archivo_plano, 10)
tail(datos_primer_archivo_plano, 10)
write.csv(datos_primer_archivo_plano, "primer_archivo_plano.csv")

Después, se pueden observar las primeras 10 y las últimas 10 observaciones del segundo archivo plano, el cual contiene el primer semestre de 2018 (01/01/2018 y el 30/06/2018). que posee 181 observaciones con dos variables.

datos_17 <- subset(diario_20_02, (YEAR == '2018'))
sem2018 <- datos_17[c(1:181),]
dat04 <- sem2018[,-2]
dat05 <- dat04[,-2]
dat06 <- dat05[,-2]
dat07 <- dat06[,-2]
datos_segundo_archivo_plano <- dat07[,-2]
head(datos_segundo_archivo_plano, 10)
tail(datos_segundo_archivo_plano, 10)
write.csv(datos_segundo_archivo_plano, "segundo_archivo_plano.csv")

Nota: Los dos archivos planos en formato .csv se pueden observar en el repositorio de Github.

LS0tDQp0aXRsZTogIlByZWRpY2Npw7NuIGRlbCBuw7ptZXJvIGRlIHZlaMOtY3Vsb3MgcmVnaXN0cmFkb3MgZW4gZWwgc2lzdGVtYSBkZSB0csOhbnNpdG8gbmFjaW9uYWwiDQphdXRob3I6ICJKZXN1cyBEYXZpZCBTYW50b3MgTW9udGVzLCBLbGVpZGVyIFN0aXZlbiBWw6FzcXVleiBHw7NtZXosIERhbmllbCBBbmRyw6lzIFRvcm8gQWd1aXJyZSwgSmVsc3NpbiBEb25ub3ZhbiBSb2JsZWRvIE1lbmEsIEp1YW4gRXN0ZWJhbiBDYXJ2YWphbC4iDQpkYXRlOiAnMTcvMDEvMjAyMicNCm91dHB1dDoNCiAgaHRtbF9kb2N1bWVudDoNCiAgICB0aGVtZTogc3BhY2VsYWINCiAgICBjb2RlX2ZvbGRpbmc6IGhpZGUNCiAgICBjb2RlX2Rvd25sb2FkOiB5ZXMNCiAgICBkZl9wcmludDogcGFnZWQNCiAgICB0b2M6IHRydWUNCiAgICB0b2NfZmxvYXQ6IA0KICAgICAgY29sYXBzZTogZmFsc2UNCi0tLQ0KDQpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0NCmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSkNCmBgYA0KDQpgYGB7ciAgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgcmVzdWx0cyA9ICdoaWRlJywgZWNobz1GICB9DQojTGlicmVyaWFzDQpsaWJyYXJ5KGR1bW1pZXMpO2xpYnJhcnkoc3RyaW5ncik7bGlicmFyeShyZWFkeGwpO2xpYnJhcnkoc2YpDQpsaWJyYXJ5KGRwbHlyKTtsaWJyYXJ5KGx1YnJpZGF0ZSk7bGlicmFyeShnZ3Bsb3QyKTtsaWJyYXJ5KEdHYWxseSkNCmxpYnJhcnkoY2FyKTtsaWJyYXJ5KE1MbWV0cmljcyk7bGlicmFyeSh3b3JkY2xvdWQpO2xpYnJhcnkoZ3Bsb3RzKQ0KbGlicmFyeShSLnV0aWxzKTtsaWJyYXJ5KHRtKTtsaWJyYXJ5KERlc2NUb29scyk7bGlicmFyeShyYXN0ZXIpDQpsaWJyYXJ5KG1jbHVzdCk7bGlicmFyeShyZ2RhbCk7bGlicmFyeShyYXN0ZXIpO2xpYnJhcnkoZ2Vvc3BoZXJlKQ0KbGlicmFyeShOYkNsdXN0KTtsaWJyYXJ5KGZhY3RvZXh0cmEpO2xpYnJhcnkodmVnYW4pO2xpYnJhcnkocXBjUikNCmxpYnJhcnkobGVhZmxldCk7bGlicmFyeShwbG90bHkpO2xpYnJhcnkoc3RyaW5naSk7bGlicmFyeShwbHlyKQ0KDQpgYGANCg0KDQojIyAxLiBJbnRyb2R1Y2Npw7NuDQoNCjxkaXYgc3R5bGU9InRleHQtYWxpZ246IGp1c3RpZnkiPiBFbiBDb2xvbWJpYSwgZWwgUmVnaXN0cm8gw5puaWNvIE5hY2lvbmFsIGRlIFRyw6Fuc2l0byBbKFJVTlQpXShodHRwczovL3NlcnZpY2lvc2RldHJhbnNpdG8uY29tL2luZGV4LnBocC9ub3RpY2lhcy8xMzktcXVlLWVzLXktY29tby1mdW5jaW9uYS1lbC1ydW50KSwgZnVuY2lvbmEgY29tbyB1bmEgZ3JhbiBiYXNlIGRlIGRhdG9zIGNlbnRyYWxpemFkYSBlbiBsYSBxdWUgc2UgYWxtYWNlbmEgaW5mb3JtYWNpw7NuIHNvYnJlIHRvZG9zIGxvcyB2ZWjDrWN1bG9zIGRlbCBwYcOtcywgbG9zIGNvbmR1Y3RvcmVzLCBsb3Mgc2VndXJvcyBkZSBlc3RvcywgbGFzIGluZnJhY2Npb25lcyBlbnRyZSBtdWNob3MgbcOhcy4gRWwgcHJlc2VudGUgcmVwb3J0ZSBhc3VtZSBlbCBlamVyY2ljaW8gZGUgY3JlYXIgdW4gbW9kZWxvIGVzdGFkw61zdGljbyBwYXJhIHByZWRlY2lyIGVsIG7Dum1lcm8gZGUgdmVow61jdWxvcyByZWdpc3RyYWRvcyBkaWFyaWFtZW50ZSBlbiBlbCBSVU5UIGR1cmFudGUgZWwgYcOxbyAyMDE4LjxkaXYvPg0KDQoNCg0KDQojIyAyLiBEYXRvcyANCg0KPGRpdiBzdHlsZT0idGV4dC1hbGlnbjoganVzdGlmeSI+DQpMYSBiYXNlIGRlIGRhdG9zIGluaWNpYWwsIHJlZ2lzdHJvX2F1dG9zX2VudHJlbmFtaWVudG8sIGVuIGZvcm1hdG8gLlhMU1gsIGNvbnRpZW5lIGxvcyByZWdpc3Ryb3MgZGlhcmlvcyBkZSBhdXRvcyBlbiBlbCBSVU5ULCBkZXNkZSBsYXMgZmVjaGFzIDEvMDEvMjAxMiBoYXN0YSAzMS8xMi8yMDE3LiBTZSB1c2Fyb24gbG9zIHNvZnR3YXJlIEV4Y2VsIHkgUiBwYXJhIGVsIHByb2Nlc2FtaWVudG8gZGUgbGEgYmFzZSBkZSBkYXRvcy4NCkluaWNpYWxtZW50ZSwgbGEgYmFzZSBkZSBkYXRvcyBjb250aWVuZSAyMTkyIG9ic2VydmFjaW9uZXMgeSAyIHZhcmlhYmxlcy4gTGFzIHZhcmlhYmxlcyBgRmVjaGFgIHkgYFVuaWRhZGVzYC4gIFVuYSB2aXN0YSBwcmV2aWEgZGUgbGFzIHByaW1lcmFzIG9ic2VydmFjaW9uZXMgZGUgbGEgYmFzZSBkZSBkYXRvcyBlcyBsYSBzaWd1aWVudGU6DQo8ZGl2Lz4NCg0KDQoNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UgLCBlY2hvPVR9DQoNCmRhdG9zIDwtIHJlYWRfZXhjZWwoInJlZ2lzdHJvc19hdXRvc19lbnRyZW5hbWllbnRvLnhsc3giKSAjY2FyZ2FyIGRhdG9zDQoNCmhlYWQoZGF0b3MsIG49NSkNCg0KDQpgYGANCg0KDQpDb24gZWwgb2JqZXRpdm8gZGUgcGxhbnRlYXIgdW4gbW9kZWxvIG3DoXMgaW50ZXJlc2FudGUsIHNlIHByb3BvbmUgbGEgY3JlYWNpw7NuIGRlIGNpbmNvIG51ZXZhcyB2YXJpYWJsZXMgcXVlIHBlcm1pdGlyw6FuIGRlc2Fycm9sbGFyIHVuIG1vZGVsbyBwcmVkaWN0aXZvIHF1ZSBhcnJvamUgbcOhcyBpbmZvcm1hY2nDs24gZW4gc3VzIHJlc3VsdGFkb3MgdGVuaWVuZG8gZW4gY3VlbnRhIGVsIGHDsW8sIG1lcywgZMOtYXMgZGVsIG1lcywgZMOtYXMgZGUgbGEgc2VtYW5hIHkgc2kgZWwgZMOtYSB0dXZvIG8gbm8gYWxndW5hIGZlc3RpdmlkYWQuIExhcyB2YXJpYWJsZXMgY3JlYWRhcyBzb246DQoNCmBZRUFSYDogYcOxbyBkZWwgcmVnaXN0cm8uIA0KYERJQWA6IGTDrWEgZGVsIG1lcyBkZWwgcmVnaXN0cm8uICANCmBNRVNgOiBtZXMgZGVsIHJlZ2lzdHJvLg0KYERJQV9TRU1BTkFgOiBkw61hIGRlbCByZWdpc3RybyAobHVuZXMgYSBkb21pbmdvKQ0KYEZFU1RJVklEQURgOiBpbmRpY2Egc2kgZWwgcmVnaXN0cm8gc2UgcmVhbGl6w7MgZW4gYWxndW5hIGRlIGxhcyBzaWd1aWVudGVzIGZlY2hhcyBlc3BlY2lhbGVzOg0KKmFfbnVldm86IDEgZGUgZW5lcm8gZGUgY2FkYSBhw7FvLg0KKmTDrWFfaGFiaWw6IGTDrWEgZW4gZG9uZGUgbm8gZXMgZmVzdGl2byBubyBoYXkgZmVzdGl2aWRhZGVzLg0KKmZlc3Rpdm86IGTDrWEgZmVzdGl2by4NCipzZW1fc2FudGE6IGTDrWEgZGUgc2VtYW5hIHNhbnRhLCBkZSBzw6FiYWRvIGEgZG9taW5nby4NCiptYWRyZXM6IGTDrWEgZGUgbGFzIG1hZHJlcy4NCipicnVqYXM6IGTDrWEgZGUgbGFzIGJydWphcywgMzEgZGUgb2N0dWJyZS4NCipuYXZpZGFkOiAyNSBkZSBkaWNpZW1icmUuDQoqYV92aWVqbzogMzEgZGUgZGljaWVtYnJlLg0KDQoNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIHJlc3VsdHMgPSAnaGlkZScsIGVjaG89Rn0NCg0KZGF0b3MkRmVjaGEgPC0gYXMuRGF0ZShkYXRvcyRGZWNoYSwgZm9ybWF0PSIlZC8lbS8lWSIpICNmb3JtYXRvIGZlY2hhDQoNCmNsYXNzKGRhdG9zJEZlY2hhKQ0KDQoNCiNhZ3JlZ2FuZG8gbnVldmFzIHZhcmlhYmxlcw0KDQpkYXRvcyR5ZWFyIDwtIHllYXIoZGF0b3MkRmVjaGEpDQpkYXRvcyRkaWEgPC0gZGF5KGRhdG9zJEZlY2hhKQ0KZGF0b3MkbWVzIDwtIG1vbnRoKGRhdG9zJEZlY2hhKQ0KZGF0b3MkZGlhX3NlbWFuYSA8LSB3ZWVrZGF5cyhkYXRvcyRGZWNoYSkNCg0KDQojZ3LDoWZpY2EgZGUgdW5pZGFkZXMgdnMgZMOtYV9zZW1hbmENCg0KDQojZGF0b3MkZGlhX3NlbWFuYSA8LSBhcy5mYWN0b3IoZGF0b3MkZGlhX3NlbWFuYSkgI2RpYSBzZW1hbmEgY29tbyBmYWN0b3INCg0KI0FncmVnYW5kbyBmZWNoYXMgZXNwZWNpYWxlcw0KDQoNCiNxdWl0YXIgdGlsZGVzDQoNCg0KZGF0b3MkZGlhX3NlbWFuYSA8LSB0b3VwcGVyKHN0cmlfdHJhbnNfZ2VuZXJhbChkYXRvcyRkaWFfc2VtYW5hLCAiTGF0aW4tQVNDSUkiKSkNCg0KI01heXVzY3VsYXMNCg0KZGF0b3MgPC0gcGx5cjo6cmVuYW1lKGRhdG9zLCAgYygiRmVjaGEiPSJGRUNIQSIsIlVuaWRhZGVzIj0iVU5JREFERVMiLCJ5ZWFyIj0iWUVBUiIsImRpYSI9IkRJQSIsIm1lcyI9Ik1FUyIsImRpYV9zZW1hbmEiPSJESUFfU0VNQU5BIikpDQoNCg0KI21pbnVzY3VsYXMgRElBX1NFTUFOQQ0KDQoNCg0KZGF0b3MkRElBX1NFTUFOQSA8LSBzdHJfdG9fbG93ZXIoZGF0b3MkRElBX1NFTUFOQSkNCg0KDQoNCmBgYA0KDQoNCg0KDQpUb2RhcyBsYXMgbnVldmFzIHZhcmlhYmxlcyBleGNlcHR1YW5kbyBgRkVTVElWSURBRGAgc2Ugb2J0aWVuZW4gZGUgbGEgdmFyaWFibGUgYEZlY2hhYC4gRW4gZWwgY2FzbyBkZSBsYSB2YXJpYWJsZSBgRkVTVElWSURBRGAsIHNlIG9idGllbmUgYSB0cmF2w6lzIHVuYSBiYXNlIGRlIGRhdG9zIGV4dGVybmEgcXVlIHNlIGFkaWNpb25hIGEgbGEgYmFzZSBkZSBhbsOhbGlzaXMgb3JpZ2luYWwgeSBhYmFyY2EgbG9zIGTDrWFzIGZlcmlhZG9zIGVuIENvbG9tYmlhIGRlc2RlIDIwMTIgaGFzdGEgMjAxOC4gIA0KDQoNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIHJlc3VsdHMgPSAnaGlkZScsIGVjaG89Rn0NCg0KIyB1bmlyIGxhcyBmZWNoYXMgZXNwZWNpYWxlcyBhIGRhdG9zLCBsb3MgcXVlIG5vIHNvbiBmZWNoYXMgZXNwZWNpYWxlcyBsb3MgcG9uZSBjb21vIE5BDQoNCmZlc3Rpdm9zIDwtIHJlYWRfZXhjZWwoImZlc3Rpdm9zLnhsc3giKQ0KDQoNCmZlc3Rpdm9zJEZFQ0hBIDwtIGFzLkRhdGUoZmVzdGl2b3MkRkVDSEEsIGZvcm1hdD0iJWQvJW0vJVkiKSAjZm9ybWF0byBmZWNoYQ0KDQoNCiN1bmllbmRvIGJhc2VzDQoNCmRhdG9zMiA8LSBtZXJnZSh4ID0gZGF0b3MsIHkgPSBmZXN0aXZvcywgYnkgPSAiRkVDSEEiLCBhbGwueCA9IFQpDQpkYXRvczIkRkVTVElWSURBRFtpcy5uYShkYXRvczIkRkVTVElWSURBRCldIDwtICJkaWFfaGFiaWwiDQoNCiNtaW51c2N1bGFzIERJQV9TRU1BTkENCg0KDQoNCmRhdG9zMiRGRVNUSVZJREFEIDwtIHN0cl90b19sb3dlcihkYXRvczIkRkVTVElWSURBRCkNCg0KDQoNCmRhdG9zMiRGRVNUSVZJREFEIDwtIGFzLmZhY3RvcihkYXRvczIkRkVTVElWSURBRCkNCg0Kc3VtbWFyeShkYXRvczIkRkVTVElWSURBRCkNCg0KDQpgYGANCg0KDQoNClBhcmEgZWZlY3RvcyBwcmFjdGljb3MgZW4gbGEgc2VjY2nDs24gZGUgbW9kZWxvIHByZWRpY3Rpdm8sIHNlIGV4cG9ydGFuIGRvcyBiYXNlcyBkZSBkYXRvczoNCg0KKipMYSBwcmltZXJhKiosIGNvbiA3IHZhcmlhYmxlcyB5IDIxOTIgb2JzZXJ2YWNpb25lcyBxdWUgdmFuIGRlc2RlIGVsIGHDsW8gMjAxMiBoYXN0YSBlbCBhw7FvIDIwMTcuDQoNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIHJlc3VsdHMgPSAnaGlkZScsIGVjaG89Rn0NCiNCQVNFIDENCnNhdmUoZGF0b3MyLCBmaWxlID0gImRhdG9zLlJEYXRhIikgI3JnZGF0YQ0Kd3JpdGUuY3N2KGRhdG9zMiwgImRhdG9zLmNzdiIpICAgICAgICAjY3N2DQoNCmBgYA0KDQoNCioqTGEgc2VndW5kYSoqLCBjb24gNiB2YXJpYWJsZXMgeSAyNTc1IG9ic2VydmFjaW9uZXMgcXVlIHZhbiBkZXNkZSBlbCBhw7FvIDIwMTIgaGFzdGEgZWwgYcOxbyAyMDE4LiBFc3RhIGJhc2UgZGUgZGF0b3Mgbm8gY29udGllbmUgbGEgdmFyaWFibGUgYFVOSURBREVTYCBkZWJpZG8gYSBxdWUgc2UgYm9ycsOzIGludGVuY2lvbmFsbWVudGUgcGFyYSBsdWVnbyByZWxsZW5hcmxhIGNvbiBsYSBwcmVkaWNjacOzbiBkZWwgbW9kZWxvLg0KDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCByZXN1bHRzID0gJ2hpZGUnLCBlY2hvPUZ9DQoNCiMgc2UgZWxhYm9yYSBkaXJlY3RhbWVudGUgZW4gZXhjZWwsIHNpbXBsZW1lbnRlIGFncmVnYW5kbyBlbCBhw7FvIDIwMTggYSBsYSBiYXNlIGRlIGRhdG9zXzEsIGJvcnJhbmRvIGxhIGNvbHVtbmEgdW5pZGFkZXMgeSBjb21wbGV0YW5kbyBsYXMgb3RyYXMgY29sdW1uYXMuDQoNCmBgYA0KDQoNCg0KDQojIyAzLiBBbsOhbGlzaXMgZGVzY3JpcHRpdm8NCg0KDQpgYGB7ciwgaW5jbHVkZT1GQUxTRX0NCmxpYnJhcnkoZ2dwdWJyKQ0KZGVzY3JpcHRpdm8gPC0gZGF0b3MyDQpgYGANCg0KYGBge3J9DQpwbG90X2x5KGRhdGEgPSBkZXNjcmlwdGl2bywgeCA9IH5GRUNIQSwgeSA9IH5VTklEQURFUywNCiAgICAgICAgdHlwZSA9ICJzY2F0dGVyIiwgbW9kZSA9ICJsaW5lcyIsIHNwbGl0ID0gfllFQVIsDQogICAgICAgIGxpbmUgPSBsaXN0KHdpZHRoID0gMSkpICU+JQ0KICBsYXlvdXQodGl0bGUgPSAnUmVnaXN0cm9zIGRlIHZlaGljdWxvcyBkaWFyaW9zIGVuIGVsIFJVTlQgZGUgMjAxMi0yMDE3JywNCiAgICAgICAgeGF4aXMgPSBsaXN0KHRpdGxlID0gIkHDsW8iKSwNCiAgICAgICAgeWF4aXMgPSBsaXN0KHRpdGxlID0gIk7Dum1lcm8gZGUgdmVoaWN1bG9zIikpDQpgYGANCg0KQXByZWNpYSBxdWUgZ3JhZHVhbG1lbnRlIHZhbiBkaXNtaW51eWVuZG8gbG9zIHJlZ2lzdHJvcyBkaWFyaW9zLCBzZSByZWdpc3RyYW4gY2FkYSB2ZXogbcOhcyB2ZWjDrWN1bG9zIGxvcyDDumx0aW1vcyBkw61hcyBkZSBjYWRhIG1lcyBkdXJhbnRlIHRvZG8gZWwgYcOxbywgZWwgcHJvbWVkaW8gZGlhcmlvIGRlIHZlaMOtY3Vsb3MgcmVnaXN0cmFkb3MgcGFyZWNlIG1hbnRlbmVyc2UgeWEgcXVlIGVsIMO6bHRpbW8gZMOtYSBkZWwgbWVzIGNvbXBlbnNhIGxhIGZhbHRhIGRlIHJlZ2lzdHJvcyBkZWwgcmVzdG8gZGUgZMOtYXMgcGVybyBlbiByZWFsaWRhZCBlbCByZWdpc3RybyBkZSB2ZWjDrWN1bG9zIHNpIGRpc21pbnV5ZSBhw7FvIHBvciBhw7FvIHlhIHF1ZSBlc3RhIGNvbXBlbnNhY2nDs24gbm8gZXMgc3VmaWNpZW50ZQ0KQWRlbcOhcywgZW4gZXN0YSBncmFmaWNhIHNlIHB1ZWRlIGFwcmVjaWFyIHVuYSBncmFuIGNhbnRpZGFkIGRlIGTDrWFzIGVuIGxvcyBjdWFsZXMgbm8gc2UgcmVnaXN0cmFuIHZlaMOtY3Vsb3MsIGVuIGxvcyBjdWFsZXMgcmVzYWx0YXIgY2FkYSAxIGRlIGVuZXJvIHkgMjUgZGUgZGljaWVtYnJlLg0KDQpgYGB7ciwgaW5jbHVkZT1GQUxTRX0NCmRlc2NyaXB0aXZvJERJQV9TRU1BTkEgPC0gYXMuZmFjdG9yKGRlc2NyaXB0aXZvJERJQV9TRU1BTkEpDQpkZXNjcmlwdGl2byRNRVMgPC0gYXMuZmFjdG9yKGRlc2NyaXB0aXZvJE1FUykNCmRlc2NyaXB0aXZvJERJQSA8LSBhcy5mYWN0b3IoZGVzY3JpcHRpdm8kRElBKQ0KZGVzY3JpcHRpdm8kWUVBUiA8LSBhcy5mYWN0b3IoZGVzY3JpcHRpdm8kWUVBUikNCmBgYA0KDQoNCmBgYHtyfQ0KZGVzY3JpcHRpdm8kRElBX1NFTUFOQSA8LSBvcmRlcmVkKGRlc2NyaXB0aXZvJERJQV9TRU1BTkEsIGxldmVscyA9IGMoImx1bmVzIiwibWFydGVzIiwibWllcmNvbGVzIiwianVldmVzIiwidmllcm5lcyIsInNhYmFkbyIsImRvbWluZ28iKSkNCg0KDQp0b3RhbHM1IDwtIGRlc2NyaXB0aXZvICU+JQ0KICBncm91cF9ieShESUFfU0VNQU5BKSAlPiUNCiAgbXV0YXRlKHRvdGFsPXN1bShVTklEQURFUykpDQp0b3RhbHM1DQoNCg0KZ2dwbG90KGRlc2NyaXB0aXZvLCBhZXMoZmlsbCA9IERJQV9TRU1BTkEsIHggPSAgRElBX1NFTUFOQSwgeSA9IFVOSURBREVTKSkgKw0KICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IikgKw0KICBnZW9tX3RleHQoZGF0YSA9IHRvdGFsczUsIGFlcyggeSA9IHRvdGFsLCBsYWJlbCA9IHRvdGFsKSwgdmp1c3QgPSAtMC41KSArDQogIHNjYWxlX3lfY29udGludW91cyhicmVha3MgPSBzZXEoIDAsNDUwMDAwLDUwMDAwKSkgKw0KICB4bGFiKCJEw61hcyIpICsgDQogIHlsYWIoIk51bWVybyBkZSB2ZWhpY3Vsb3MiKSArIA0KICBnZ3RpdGxlKCJSZWdpc3RybyBkZSB2ZWhpY3Vsb3MgcG9yIGTDrWEgZGUgbGEgc2VtYW5hIikgKw0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpDQpgYGANCg0KUG9kZW1vcyBhcHJlY2lhciBxdWUgZGUgbWnDqXJjb2xlcyBhIHZpZXJuZXMgc2UgaGFjZSBsYSBtYXlvcsOtYSBkZSBsb3MgcmVnaXN0cm9zLCBjb24gZWwgdmllcm5lcyBjb21vIGVsIGTDrWEgZGUgbGEgc2VtYW5hIGNvbiBtYXlvciBjYW50aWRhZCBkZSB2ZWjDrWN1bG9zIHJlZ2lzdHJhZG9zLCBzZSBwdWVkZSBvYnNlcnZhciBjb21vIGhheSBtZW5vcyByZWdpc3Ryb3MgZW4gbG9zIGZpbmVzIGRlIHNlbWFuYSBlbiBjb21wYXJhY2nDs24gYSBsb3MgZMOtYXMgZGUgbGEgc2VtYW5hLCBhZGVtYXMgZGUgcXVlIGVsIGRvbWluZ28gZXMgZWwgZMOtYSBjb24gbWVub3IgY2FudGlkYWQgZGUgcmVnaXN0cm9zIGRlIHZlaMOtY3Vsb3MuDQoNCmBgYHtyfQ0KdG90YWxzNCA8LSBkZXNjcmlwdGl2byAlPiUNCiAgZ3JvdXBfYnkoTUVTKSAlPiUNCiAgbXV0YXRlKHRvdGFsPXN1bShVTklEQURFUykpDQoNCg0KZ2dwbG90KGRlc2NyaXB0aXZvLCBhZXMoZmlsbCA9IE1FUywgeCA9IGFzLmZhY3RvcihNRVMpLCB5ID0gVU5JREFERVMpKSArIA0KICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IikgKw0KICBnZW9tX3RleHQoZGF0YSA9IHRvdGFsczQsIGFlcyggeSA9IHRvdGFsLCBsYWJlbCA9IHRvdGFsKSwgdmp1c3QgPSAtMC41KSArDQogIHNjYWxlX3lfY29udGludW91cyhicmVha3MgPSBzZXEoIDAsMjAwMDAwLDI1MDAwKSkgKw0KICBzY2FsZV9maWxsX3ZpcmlkaXNfZCggb3B0aW9uID0gIkciKSArDQogIHhsYWIoIk1lcyIpICsNCiAgeWxhYigiTnVtZXJvIGRlIHZlaGljdWxvcyIpICsNCiAgZ2d0aXRsZSgiUmVnaXN0cm8gZGUgdmVoaWN1bG9zIHBvciBtZXMiKSsNCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQ0KYGBgDQoNCkRpY2llbWJyZSBlcyBlbCBtZXMgY29uIG1heW9yIGNhbnRpZGFkIGRlIHJlZ2lzdHJvcyBkZSB2ZWjDrWN1bG9zIHkgZW5lcm8gZWwgbWVzIGNvbiBtZW5vciBjYW50aWRhZCBkZSByZWdpc3Ryb3MsIGlnbm9yYW5kbyBlc3RvcyBkb3MgZGF0b3MgeSBzb2xvIHRvbWFuZG8gZGVsIG1lcyBmZWJyZXJvIGFsIG1lcyBkZSBub3ZpZW1icmUgbG9zIHJlZ2lzdHJvcyBkZSB2ZWjDrWN1bG9zIGVuIGVsIFJVTlQgZXN0w6FuIGVudHJlIDEzMDAwMCB5IDE1MDAwMCwgdmFyaWFuZG8gcG9yIHVub3MgY3VhbnRvcyBtaWxlcyBkZSB2ZWjDrWN1bG9zIA0KDQoNCmBgYHtyfQ0KdG90YWxzMyA8LSBkZXNjcmlwdGl2byAlPiUNCiAgZ3JvdXBfYnkoWUVBUikgJT4lDQogIG11dGF0ZSh0b3RhbD1zdW0oVU5JREFERVMpKQ0KDQoNCmdncGxvdChkZXNjcmlwdGl2bywgYWVzKGZpbGwgPSBZRUFSLCB4ID0gYXMuZmFjdG9yKFlFQVIpLCB5ID0gVU5JREFERVMpKSArIA0KICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IikgKw0KICBnZW9tX3RleHQoZGF0YSA9IHRvdGFsczMsIGFlcyggeSA9IHRvdGFsLCBsYWJlbCA9IHRvdGFsKSwgdmp1c3QgPSAtMC41KSArDQogIHNjYWxlX3lfY29udGludW91cyhicmVha3MgPSBzZXEoIDAsNDAwMDAwLDI1MDAwKSkgKw0KICBzY2FsZV9maWxsX3ZpcmlkaXNfZCggb3B0aW9uID0gIkUiKSArDQogIHhsYWIoIkHDsW8iKSArDQogIHlsYWIoIk51bWVybyBkZSB2ZWhpY3Vsb3MiKSArDQogIGdndGl0bGUoIlJlZ2lzdHJvIGRlIHZlaGljdWxvcyAgcG9yIGHDsW8iKSsNCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQ0KYGBgDQoNCkVsIGHDsW8gMjAxNCBlcyBlbCBhw7FvIGNvbiBtw6FzIHZlaMOtY3Vsb3MgcmVnaXN0cmFkb3MsIGlnbm9yYW5kbyBlc3RlIGRhdG8gcG9kcsOtYSBkZWNpcnNlIHF1ZSBjYWRhIGHDsW8gc2UgcmVnaXN0cmFuIG1lbm9zIHZlaMOtY3Vsb3MuDQoNCg0KDQpgYGB7ciwgaW5jbHVkZT1GQUxTRX0NCmRlc2NyaXB0aXZvJGRpYV9oYWJpbCA8LSBpZmVsc2UoZGVzY3JpcHRpdm8kRkVTVElWSURBRCA9PSAiZGlhX2hhYmlsIiwgIlNJIiwgIk5PIikNCmRlc2NyaXB0aXZvJGRpYV9oYWJpbCA8LSBhcy5mYWN0b3IoZGVzY3JpcHRpdm8kZGlhX2hhYmlsKQ0KYGBgDQoNCmBgYHtyLCBpbmNsdWRlPUZBTFNFfQ0KTk9fZmVzIDwtIGRlc2NyaXB0aXZvICU+JQ0KICBmaWx0ZXIoRkVTVElWSURBRCA9PSAiZGlhX2hhYmlsIikNCk5PX2Zlcw0KYGBgDQoNCmBgYHtyLCBpbmNsdWRlPUZBTFNFfQ0Kc29sb190aXBvX2Zlc3RpdmlkYWQgPC0gZGVzY3JpcHRpdm8gJT4lDQogIGFudGlfam9pbihOT19mZXMpDQpzdW1tYXJ5KHNvbG9fdGlwb19mZXN0aXZpZGFkKQ0KYGBgDQoNCmBgYHtyLCBpbmNsdWRlPUZBTFNFfQ0Kc29sb190aXBvX2Zlc3RpdmlkYWQkRkVTVElWSURBRCA8LSBnc3ViKCJhX251ZXZvIiwiQcORTyBOVUVWTyIsIHNvbG9fdGlwb19mZXN0aXZpZGFkJEZFU1RJVklEQUQpDQpzb2xvX3RpcG9fZmVzdGl2aWRhZCRGRVNUSVZJREFEIDwtIGdzdWIoImFfdmllam8iLCJBw5FPIFZJRUpPIiwgc29sb190aXBvX2Zlc3RpdmlkYWQkRkVTVElWSURBRCkNCnNvbG9fdGlwb19mZXN0aXZpZGFkJEZFU1RJVklEQUQgPC0gZ3N1YigibWFkcmVzIiwiRElBIERFIExBUyBNQURSRVMiLCBzb2xvX3RpcG9fZmVzdGl2aWRhZCRGRVNUSVZJREFEKQ0Kc29sb190aXBvX2Zlc3RpdmlkYWQkRkVTVElWSURBRCA8LSBnc3ViKCJicnVqYXMiLCJIQUxMT1dFRU4iLCBzb2xvX3RpcG9fZmVzdGl2aWRhZCRGRVNUSVZJREFEKQ0Kc29sb190aXBvX2Zlc3RpdmlkYWQkRkVTVElWSURBRCA8LSBnc3ViKCJzZW1fc2FudGEiLCJTRU1BTkEgU0FOVEEiLCBzb2xvX3RpcG9fZmVzdGl2aWRhZCRGRVNUSVZJREFEKQ0Kc29sb190aXBvX2Zlc3RpdmlkYWQkRkVTVElWSURBRCA8LSBnc3ViKCJmZXN0aXZvIiwiRkVTVElWTyIsIHNvbG9fdGlwb19mZXN0aXZpZGFkJEZFU1RJVklEQUQpDQpzb2xvX3RpcG9fZmVzdGl2aWRhZCRGRVNUSVZJREFEIDwtIGdzdWIoIm5hdmlkYWQiLCJOQVZJREFEIiwgc29sb190aXBvX2Zlc3RpdmlkYWQkRkVTVElWSURBRCkNCmBgYA0KDQpgYGB7cn0NCnRvdGFsczIgPC0gZGVzY3JpcHRpdm8gJT4lDQogIGdyb3VwX2J5KGRpYV9oYWJpbCkgJT4lDQogIG11dGF0ZSh0b3RhbD1zdW0oVU5JREFERVMpKQ0KDQpnZmVzMSA8LSBnZ3Bsb3QoZGVzY3JpcHRpdm8sIGFlcyhmaWxsID0gZGlhX2hhYmlsLCB4ID0gZGlhX2hhYmlsLCB5ID0gVU5JREFERVMpKSArDQogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiKSArDQogIGdlb21fdGV4dChkYXRhID0gdG90YWxzMiwgYWVzKHggPSBkaWFfaGFiaWwsIHkgPSB0b3RhbCwgbGFiZWwgPSB0b3RhbCksIHZqdXN0ID0gLTAuNSkgKw0KICBzY2FsZV95X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKCAwLDIyNTAwMDAsMjUwMDAwKSkgKw0KICB4bGFiKCJkaWEgaGFiaWwiKSArDQogIHlsYWIoIk51bWVybyBkZSB2ZWhpY3Vsb3MiKSArDQogIGdndGl0bGUoInJlZ2lzdHJvcyBkZSBkaWFzIGhhYmlsZXMgeSBubyBoYWJpbGVzIikrDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikNCg0KdG90YWxzIDwtIHNvbG9fdGlwb19mZXN0aXZpZGFkICU+JQ0KICBncm91cF9ieShGRVNUSVZJREFEKSAlPiUNCiAgbXV0YXRlKHRvdGFsPXN1bShVTklEQURFUykpDQoNCg0KZ2ZlczIgPC0gZ2dwbG90KHNvbG9fdGlwb19mZXN0aXZpZGFkLCBhZXMoZmlsbCA9IEZFU1RJVklEQUQsIHggPSByZW9yZGVyKEZFU1RJVklEQUQsK1VOSURBREVTKSwgeSA9IFVOSURBREVTKSkgKw0KICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IikgKw0KICBzY2FsZV9maWxsX3ZpcmlkaXNfZCggb3B0aW9uID0gIkQiKSArDQogIGdlb21fdGV4dChkYXRhID0gdG90YWxzLCBhZXMoeCA9IEZFU1RJVklEQUQsIHkgPSB0b3RhbCwgbGFiZWwgPSB0b3RhbCksIHZqdXN0ID0gLTAuNSkgKw0KICB4bGFiKCJmZXN0aXZpZGFkIikgKw0KICB5bGFiKCJOdW1lcm8gZGUgdmVoaWN1bG9zIikgKw0KICBzY2FsZV95X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKCAwLDMwMDAwLDI1MDApKSArDQogIGdndGl0bGUoIlJlZ2lzdHJvcyBlbiBsYXMgZmVzdGl2aWRhZGVzIikgKw0KICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBoanVzdCA9IDApLCBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpDQoNCg0KZ2dhcnJhbmdlKGdmZXMxLGdmZXMyKQ0KYGBgDQoNCkEgY29tcGFyYWNpw7NuIGRlIGxvcyBkw61hcyBow6FiaWxlcywgbGEgc3VtYSBkZSB2ZWjDrWN1bG9zIHJlZ2lzdHJhZG8gZW4gbG9zIGTDrWFzIGZlc3Rpdm9zIHkgZGUgZmVzdGl2aWRhZGVzIGVzIG11eSBwb2NhLg0KTG9zIGRvcyB1bmljb3MgZMOtYXMgcXVlIG5vIHByZXNlbnRhbiBuaW5nw7puIHJlZ2lzdHJvIGRlIHZlaMOtY3Vsb3MgZW4gZWwgUlVOVCBkZSAyMDEyIGEgMjAxNyBzb24gYcOxbyBudWV2byhwcmltZXJvIGRlIGVuZXJvKSB5IG5hdmlkYWQoMjUgZGUgZGljaWVtYnJlKS4gDQoNClNpZW5kbyBtdWNob3MgbWVub3MgbG9zIGTDrWFzIGZlc3Rpdm9zIGNvbXVuZXMgcXVlIGxvcyBkw61hcyBjZWxlYnJhY2nDs24gZGUgbGEgc2VtYW5hIHNhbnRhIHNlIHJlZ2lzdHJhbiBtdWNob3MgbcOhcyB2ZWjDrWN1bG9zIGVuIHNlbWFuYSBzYW50YSwgcHVlZGUgaGFjZXJzZSB1bmEgY29tcGFyYWNpw7NuIHBhcmVjaWRhIGNvbiBlbCBkw61hIGRlIG1hZHJlcyB5IGhhbG93ZWVuLCBzaWVuZG8gc29sbyB1biBkw61hIGhheSB1bmEgZW5vcm1lIGRpZmVyZW5jaWEgZW4gbG9zIHJlZ2lzdHJvcyBzaWVuZG8gZWwgZMOtYSBkZSBtYWRyZXMgMiByZWdpc3Ryb3MgYWwgc2VyIGTDrWEgZG9taW5nbyB5IGVuIGhhbG93ZWVuIG3DoXMgZGUgMTFtaWwgYWwgc2VyIGNhc2kgc2llbXByZSB1biBkw61hIG5vcm1hbCBlbiBzZW1hbmEuDQoNCg0KDQoNCiMjIDQuIE1vZGVsbyBQcmVkaWN0aXZvDQoNCg0KU2UgbGxldmEgYSBjYWJvIHVuIE1vZGVsbyBsaW5lYWwgZ2VuZXJhbGl6YWRvIGNvbiBsYSBmYW1pbGlhIGRlIGRpc3RyaWJ1Y2nDs24gUG9pc3NvbiwgZG9uZGUgbGEgdmFyaWFibGUgcmVzcHVlc3RhICh2YXJpYWJsZSBkZXBlbmRpZW50ZSkgc2Vyw61hICJVTklEQURFUyIsIHkgbGFzIHZhcmlhYmxlcyBleHBsaWNhdGl2YXMgKHZhcmlhYmxlcyBpbmRlcGVuZGllbnRlcykgc29uOiBGRUNIQSwgWUVBUiwgRElBLCBNRVMsIERJQV9TRU1BTkEsIHkgRkVTVElWSURBRC4NCg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgcmVzdWx0cyA9ICdoaWRlJywgZWNobz1GIH0NCmxvc19kYXRvcyA8LSByZWFkLmNzdigiZGF0b3NfMS5jc3YiLCBzZXA9IjsiLCBlbmNvZGluZz0iVVRGLTgiKQ0KDQpsb3NfZGF0b3MkRkVDSEEgPC0gYXMuRGF0ZShsb3NfZGF0b3MkRkVDSEEsIGZvcm1hdD0iJWQvJW0vJVkiKQ0KDQpsb3NfZGF0b3MkVU5JREFERVMgPC0gYXMuaW50ZWdlcihsb3NfZGF0b3MkVU5JREFERVMpDQoNCmxvc19kYXRvcyRZRUFSIDwtIGFzLmludGVnZXIobG9zX2RhdG9zJFlFQVIpDQoNCmxvc19kYXRvcyRESUEgPC0gYXMuaW50ZWdlcihsb3NfZGF0b3MkRElBKQ0KDQpsb3NfZGF0b3MkTUVTIDwtIGFzLmludGVnZXIobG9zX2RhdG9zJE1FUykNCg0KbG9zX2RhdG9zJERJQV9TRU1BTkE8LSBhcy5mYWN0b3IobG9zX2RhdG9zJERJQV9TRU1BTkEpDQoNCmxvc19kYXRvcyRGRVNUSVZJREFEIDwtIGFzLmZhY3Rvcihsb3NfZGF0b3MkRkVTVElWSURBRCkNCg0KYGBgDQoNCkRvbmRlIHNlIHV0aWxpemFyw6FuIGxvcyBhw7FvcyBkZXNkZSAyMDEyIGhhc3RhIDIwMTYgcGFyYSBsYSBjcmVhY2nDs24gZGVsIG1vZGVsby4NCg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgcmVzdWx0cyA9ICdoaWRlJywgZWNobz1GIH0NCiNEYXRvcyBwYXJhIGVsIG1vZGVsbyAyMDEyLTIwMTYNCg0KbG9zZGF0b3MgPC0gbG9zX2RhdG9zW2MoMToxODI3KSxdDQoNCmRhdG9zX2xtMiA8LSBsb3NkYXRvcyAlPiUgZ3JvdXBfYnkoRkVDSEEsIFlFQVIsIERJQSwgTUVTLCBESUFfU0VNQU5BLCBGRVNUSVZJREFEKQ0KDQpsbTIgPC0gZ2xtKFVOSURBREVTIH4gRkVDSEErWUVBUitESUErTUVTK0RJQV9TRU1BTkErRkVTVElWSURBRCwgZmFtaWx5ID0gInBvaXNzb24iLCBkYXRhID0gZGF0b3NfbG0yKQ0KDQpgYGANCg0KU2UgcHJvY2VkZSBhIGhhY2VyIGxlY3R1cmEgZGUgbGEgYmFzZSBkZSBkYXRvcyBlbiBkb25kZSBzZSBhbG1hY2VuYXLDoW4gdG9kYXMgbGFzIHByZWRpY2Npb25lcywgc2Vnw7puIGVsIG1vZGVsbyBhbnRlcmlvci4NCg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgcmVzdWx0cyA9ICdoaWRlJywgZWNobz1GIH0NCkJhc2VfcHJlZGljY2lvbiA8LSByZWFkLmNzdigicHJlZGljY2lvbl90MDIyXzEuY3N2Iiwgc2VwID0gIjsiLCBlbmNvZGluZyA9ICJVVEYtOCIpDQoNCkJhc2VfcHJlZGljY2lvbiRGRUNIQSA8LSBhcy5EYXRlKEJhc2VfcHJlZGljY2lvbiRGRUNIQSwgZm9ybWF0PSIlZC8lbS8lWSIpDQoNCkJhc2VfcHJlZGljY2lvbiRZRUFSIDwtIGFzLmludGVnZXIoQmFzZV9wcmVkaWNjaW9uJFlFQVIpDQoNCkJhc2VfcHJlZGljY2lvbiRESUEgPC0gYXMuaW50ZWdlcihCYXNlX3ByZWRpY2Npb24kRElBKQ0KDQpCYXNlX3ByZWRpY2Npb24kTUVTIDwtIGFzLmludGVnZXIoQmFzZV9wcmVkaWNjaW9uJE1FUykNCg0KQmFzZV9wcmVkaWNjaW9uJERJQV9TRU1BTkE8LSBhcy5mYWN0b3IoQmFzZV9wcmVkaWNjaW9uJERJQV9TRU1BTkEpDQoNCkJhc2VfcHJlZGljY2lvbiRGRVNUSVZJREFEIDwtIGFzLmZhY3RvcihCYXNlX3ByZWRpY2Npb24kRkVTVElWSURBRCkNCg0KYGBgDQoNClNlIG11ZXN0cmFuIGxhcyBwcmltZXJhcyAxMCBvYnNlcnZhY2lvbmVzIGRlIGxhcyBwcmVkaWNjaW9uZXMgZGFkYXMuIEFsbWFjZW5hZGFzIGVuIGxhIHZhcmlhYmxlIE5ST19UT1RBTF9WRUhJQ1VMT1MsIHF1ZSByZXByZXNlbnRhIGVsIG7Dum1lcm8gZGUgdmVow61jdWxvcyByZWdpc3RyYWRvcy4NCg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSAsIGVjaG89VCB9DQpwcmVkaWNjaW9uIDwtIHByZWRpY3Qob2JqZWN0ID0gbG0yLCBuZXdkYXRhID0gQmFzZV9wcmVkaWNjaW9uLA0KICAgICAgICAgICAgICAgICAgICAgICAgICB0eXBlID0gInJlc3BvbnNlIikNCg0KcHJlZGljY2lvbl9kaWFyaWE8LSBCYXNlX3ByZWRpY2Npb24gJT4lIA0KICBtdXRhdGUoTnJvX1ZlaGljdWxvcyA9IHJvdW5kKHByZWRpY2Npb24sMCkpDQoNCmRpYXJpb18yMF8wMiA8LSBwcmVkaWNjaW9uX2RpYXJpYSAlPiUNCiAgZ3JvdXBfYnkoRkVDSEEsIFlFQVIsIERJQSwgTUVTLCBESUFfU0VNQU5BLCBGRVNUSVZJREFEKSAlPiUNCiAgbXV0YXRlKE5ST19UT1RBTF9WRUhJQ1VMT1M9TnJvX1ZlaGljdWxvcykNCg0KZGlhcmlvXzIwXzAyJE5ST19UT1RBTF9WRUhJQ1VMT1MgPC0gYXMuaW50ZWdlcihkaWFyaW9fMjBfMDIkTlJPX1RPVEFMX1ZFSElDVUxPUykNCg0KaGVhZChkaWFyaW9fMjBfMDIsIDEwKQ0KYGBgDQoNCkx1ZWdvLCBzZSBvYnNlcnZhIGVsIE1TRSwgTUFFIHkgJFJeMiQgcGFyYSBsb3MgZGF0b3MgZGUgZW50cmVuYW1pZW50byAocXVlIHBvc2VlIGxvcyBkw61hcyBjb21wcmVuZGlkb3MgZW50cmUgZWwgMDEvMDEvMjAxMiB5IGVsIDMxLzEyLzIwMTYpLg0KDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFICwgZWNobz1UIH0NCiNFbnRyZW5hbW9zIGNvbiAyMDEyLTIwMTYNCg0KZGF0b3NfMDIgPC0gc3Vic2V0KGxvc19kYXRvcywgKFlFQVIgIT0gJzIwMTcnKSkNCg0KeV90cmFpbiA8LSByb3VuZChwcmVkaWN0KGxtMiwgbmV3ZGF0YT0gZGF0b3NfMDIsIHR5cGU9InJlc3BvbnNlIikpDQp5X2FjdHVhbCA8LSBkYXRvc18wMiRVTklEQURFUw0KbG03X3Rtc2UgPC0gTVNFKHlfdHJhaW4sIHlfYWN0dWFsKQ0KbG03X3RtYWUgPC0gIE1BRSh5X3RyYWluLCB5X2FjdHVhbCkNCmxtN19yMiA8LSBSMl9TY29yZSh5X3RyYWluLCB5X2FjdHVhbCkNCnNwcmludGYoIk1TRTogJWYsIE1BRTogJWYsIFIyIFNjb3JlOiAlZiIsIA0KICAgICAgICBsbTdfdG1zZSwgbG03X3RtYWUsIGxtN19yMikNCmBgYA0KUG9yIGNvbnNpZ3VpZW50ZSwgc2Ugb2JzZXJ2YSBlbCBNU0UsIE1BRSB5ICRSXjIkIHBhcmEgbG9zIGRhdG9zIGRlIHZhbGlkYWNpw7NuIChxdWUgcG9zZWUgbG9zIGTDrWFzIGNvbXByZW5kaWRvcyBlbnRyZSBlbCAwMS8wMS8yMDE3IHkgZWwgMzEvMTIvMjAxNykuDQoNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UgLCBlY2hvPVQgfQ0KI1ZhbGlkYWNpw7NuIGNvbiAyMDE3DQoNCmRhdG9zX3ZsIDwtIHN1YnNldChsb3NfZGF0b3MsIChZRUFSID09ICcyMDE3JykpDQoNCnlfdHJhaW4gPC0gcm91bmQocHJlZGljdChsbTIsIG5ld2RhdGE9IGRhdG9zX3ZsLCB0eXBlPSJyZXNwb25zZSIpKQ0KeV9hY3R1YWwgPC0gZGF0b3NfdmwkVU5JREFERVMNCmxtN190bXNlIDwtIE1TRSh5X3RyYWluLCB5X2FjdHVhbCkNCmxtN190bWFlIDwtICBNQUUoeV90cmFpbiwgeV9hY3R1YWwpDQpsbTdfcjIgPC0gUjJfU2NvcmUoeV90cmFpbiwgeV9hY3R1YWwpDQpzcHJpbnRmKCJNU0U6ICVmLCBNQUU6ICVmLCBSMiBTY29yZTogJWYiLCBsbTdfdG1zZSwgbG03X3RtYWUsIGxtN19yMikNCmBgYA0KDQpUYW1iacOpbiBzZSBjYWxjdWxhIGVsIE1TRSwgTUFFIHkgJFJeMiQgcGFyYSBlbCBwcmltZXIgc2VtZXN0cmUgZGUgMjAxNy4NCg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSAsIGVjaG89VCB9DQojIFByaW1lciBzZW1lc3RyZSBkZWwgMjAxNw0KDQpkYXRvc192bCA8LSBzdWJzZXQobG9zX2RhdG9zLCAoWUVBUiA9PSAnMjAxNycpKQ0Kc2VtMjAxNzwtIGRhdG9zX3ZsW2MoMToxODEpLF0NCg0KeV90cmFpbiA8LSByb3VuZChwcmVkaWN0KGxtMiwgbmV3ZGF0YT0gc2VtMjAxNywgdHlwZT0icmVzcG9uc2UiKSkNCnlfYWN0dWFsIDwtIHNlbTIwMTckVU5JREFERVMNCmxtN190bXNlIDwtIE1TRSh5X3RyYWluLCB5X2FjdHVhbCkNCmxtN190bWFlIDwtICBNQUUoeV90cmFpbiwgeV9hY3R1YWwpDQpsbTdfcjIgPC0gUjJfU2NvcmUoeV90cmFpbiwgeV9hY3R1YWwpDQpzcHJpbnRmKCJNU0U6ICVmLCBNQUU6ICVmLCBSMiBTY29yZTogJWYiLCBsbTdfdG1zZSwgbG03X3RtYWUsIGxtN19yMikNCmBgYA0KDQpMdWVnbywgZWwgJFJeMiQgZGVsIGNvbmp1bnRvIGRlIHZhbGlkYWNpw7NuIHF1ZSBjb21wcmVuZGUgZWwgcHJpbWVyIHNlbWVzdHJlIGRlbCAyMDE4ICgwMS8wMS8yMDE4IHkgZWwgMzAvMDYvMjAxOCksIG5vIHNlIGNhbGN1bGEgeWEgcXVlIHBhcmEgZWwgMjAxOCBubyBzZSBvYnRpZW5lbiBvYnNlcnZhY2lvbmVzIHJlYWxlcyBwYXJhIGRpY2hvIGHDsW8uDQoNCkEgY29udGludWFjacOzbiwgc2UgYXByZWNpYW4gbGFzIHByaW1lcmFzIDEwIHkgbGFzIMO6bHRpbWFzIDEwIG9ic2VydmFjaW9uZXMgZGVsIHByaW1lciBhcmNoaXZvIHBsYW5vICgwMS8wMS8yMDEyIHkgZWwgMzEvMTIvMjAxNyksIHF1ZSBjb250aWVuZSAxODI3IG9ic2VydmFjaW9uZXMgY29uIGRvcyB2YXJpYWJsZXMuDQoNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UgLCBlY2hvPVQgfQ0KZGF0b3NfMTYgPC0gc3Vic2V0KGRpYXJpb18yMF8wMiwgKFlFQVIgIT0gJzIwMTgnKSkNCmRhdCA8LSBkYXRvc18xNlssLTJdDQpkYXQwMSA8LSBkYXRbLC0yXQ0KZGF0MDIgPC0gZGF0MDFbLC0yXQ0KZGF0MDMgPC0gZGF0MDJbLC0yXQ0KZGF0b3NfcHJpbWVyX2FyY2hpdm9fcGxhbm8gPC0gZGF0MDNbLC0yXQ0KaGVhZChkYXRvc19wcmltZXJfYXJjaGl2b19wbGFubywgMTApDQoNCmBgYA0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSAsIGVjaG89VCB9DQp0YWlsKGRhdG9zX3ByaW1lcl9hcmNoaXZvX3BsYW5vLCAxMCkNCmBgYA0KDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFICwgZWNobz1UIH0NCndyaXRlLmNzdihkYXRvc19wcmltZXJfYXJjaGl2b19wbGFubywgInByaW1lcl9hcmNoaXZvX3BsYW5vLmNzdiIpDQpgYGANCg0KRGVzcHXDqXMsIHNlIHB1ZWRlbiBvYnNlcnZhciBsYXMgcHJpbWVyYXMgMTAgeSBsYXMgw7psdGltYXMgMTAgb2JzZXJ2YWNpb25lcyBkZWwgc2VndW5kbyBhcmNoaXZvIHBsYW5vLCBlbCBjdWFsIGNvbnRpZW5lIGVsIHByaW1lciBzZW1lc3RyZSBkZSAyMDE4ICgwMS8wMS8yMDE4IHkgZWwgMzAvMDYvMjAxOCkuIHF1ZSBwb3NlZSAxODEgb2JzZXJ2YWNpb25lcyBjb24gZG9zIHZhcmlhYmxlcy4NCg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSAsIGVjaG89VCB9DQpkYXRvc18xNyA8LSBzdWJzZXQoZGlhcmlvXzIwXzAyLCAoWUVBUiA9PSAnMjAxOCcpKQ0Kc2VtMjAxOCA8LSBkYXRvc18xN1tjKDE6MTgxKSxdDQpkYXQwNCA8LSBzZW0yMDE4WywtMl0NCmRhdDA1IDwtIGRhdDA0WywtMl0NCmRhdDA2IDwtIGRhdDA1WywtMl0NCmRhdDA3IDwtIGRhdDA2WywtMl0NCmRhdG9zX3NlZ3VuZG9fYXJjaGl2b19wbGFubyA8LSBkYXQwN1ssLTJdDQpoZWFkKGRhdG9zX3NlZ3VuZG9fYXJjaGl2b19wbGFubywgMTApDQpgYGANCg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSAsIGVjaG89VCB9DQp0YWlsKGRhdG9zX3NlZ3VuZG9fYXJjaGl2b19wbGFubywgMTApDQpgYGANCg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSAsIGVjaG89VCB9DQp3cml0ZS5jc3YoZGF0b3Nfc2VndW5kb19hcmNoaXZvX3BsYW5vLCAic2VndW5kb19hcmNoaXZvX3BsYW5vLmNzdiIpDQpgYGANCg0KKipOb3RhOioqIExvcyBkb3MgYXJjaGl2b3MgcGxhbm9zIGVuIGZvcm1hdG8gLmNzdiBzZSBwdWVkZW4gb2JzZXJ2YXIgZW4gZWwgcmVwb3NpdG9yaW8gZGUgR2l0aHViLg0KDQoNCg0KDQoNCg==